今天這篇跟 Laravel 沒有什麼關係,各個程式語言都有 閉包 (closure) 的使用,我自己是在看 Laravel 文件時才第一次看到,對當時的我來說檢視是每個英文字都懂,實際在幹嘛完全不知道…..因此今天先來講講 閉包(closure) 。
本篇文章使用強者我同學正正的筆記,與 GhatGpt 協助文字描述,並增加一些自己的圖解。
閉包(Closure)是一種在程式設計中常見的概念,特別是在函數式程式設計和某些程式語言中,如JavaScript、Python、Swift等,它具有特殊的含義和用途。
閉包是一個函數,它包含了它被建立時所處的作用域(或者說環境)中的變數的引用,即使在建立它的作用域外部呼叫這個函數時,它仍然能夠訪問這些變數。這意味著閉包可以捕獲並保留外部作用域中的狀態信息,使得函數在不同的上下文中執行時能夠保持一致的行為。
通常,閉包由以下幾個要素組成:
閉包的主要用途包括:
在不同的程式語言中,閉包的具體語法和用法可能有所不同,但基本概念是通用的。閉包是一種強大的程式設計工具,可以使程式碼更加靈活和可複用。
// 這是一個接受三個參數的函數。前兩個參數 $a 和 $b 是要進行操作的數字,而第三個參數 $operation 是一個函數,用來執行特定的計算操作。
function calculate($a, $b, $operation) {
return $operation($a, $b);
}
// 定義 $addition 匿名函數
// 這裡定義了一個匿名函數(無名函數),這個匿名函數接受兩個參數 $a 和 $b,並返回它們的和再加上 5。這個匿名函數實現了一個簡單的加法操作,但多了一個固定的加數 5。
$addition = function($a, $b) {
return $a + $b + 5;
};
// 使用 calculate 函數執行計算:
// 在這裡,我們使用 calculate 函數,將數字 10 和 7 以及 $addition 匿名函數作為參數傳遞給它。$addition 函數被傳遞給 calculate 函數,並在 calculate 函數內部被稱為 $operation。
$result = calculate(10, 7, $addition);
上面舉例可以寫成更簡便的閉包
function calculate($a, $b, $operation) {
return $operation($a, $b);
}
// 更簡化一點
$result = calculate(10, 7, function($a, $b) {
return $a + $b + 5;
});
echo $result; // 22
在這個版本中,我們將匿名函數直接傳遞給 calculate
函數,而不需要事先定義 $addition
函數。這樣可以減少不必要的中間步驟,使程式碼更加簡潔和易讀。這個匿名函數在 calculate
函數內部被稱為 $operation
,然後執行相同的加法操作,返回結果為 22。
我們可以將程式碼分為兩個作用域:外部作用域(Outer Scope)和內部作用域(Inner Scope)。外部作用域是閉包被建立的地方,而內部作用域是閉包內部的函數體。
當你需要對一個現有的功能進行擴展,但不能修改原來的程式碼或需要保持它的原始功能,這時你可以使用閉包的方法。閉包可以讓你新增額外的功能,而不影響原本的功能。這樣你就可以在不改變原本的功能的情況下,為它添加新的功能或自定義行為。
當需要對一組數據進行特定操作,但這個操作可能會在不同地方多次使用,閉包(匿名函數)就可以派上用場。
// 定義一個閉包,用於計算數字的平方
$square = function($number) {
return $number * $number;
};
// 定義通用的資料運算函數,接受閉包作為參數
function calculate($operation, $data) {
$result = [];
foreach ($data as $number) {
$result[] = $operation($number);
}
return $result;
}
// 假設我們有一組數據
$data = [1, 2, 3, 4, 5];
// 使用 calculate 函數,傳入閉包 $square 和數據
$numbersSquared = calculate($square, $data);
// 輸出運算結果
print_r($numbersSquared);
這個例子中,我們首先定義了 $square
閉包,然後,我們定義了 calculate
函數,它接受一個資料陣列和一個閉包函數作為參數,然後對資料陣列應用閉包操作。這樣的設計讓程式碼更容易理解,因為函數的用途和功能更清晰。
閉包除了在函式中塞入另一個函式外,也可以取得外部變數,一起放進肚子裡進行處理。
$outerVar = 10; // 外部作用域的變數
// 創建一個閉包,使用 use 捕獲外部變數 $outerVar
$myClosure = function ($innerVar) use ($outerVar) {
return $innerVar + $outerVar;
};
// 呼叫閉包,傳入內部變數 $innerVar
$result = $myClosure(5);
echo $result; // 輸出 15,因為 $innerVar(5) + $outerVar(10) = 15
在這個例子中:
$myClosure
,它接受一個參數 $innerVar
,同時使用 use
捕獲了外部變數 $outerVar
。$innerVar
的值為 5
。$innerVar + $outerVar
,所以最終結果是 5 + 10 = 15
。這個例子演示了如何在閉包中使用 use
來捕獲外部變數,以便在閉包中使用它們。這種技巧在許多情況下非常有用,例如當你需要在閉包中訪問外部變數時。
不過這種情況並不是必須的,只是一種寫法。使用use只是把外部變數放進來比較好閱讀的一種方式而已,並不代表會有一種狀況一定需要使用use,我也可以把所有需要參數放進function裏面。
// 時間搜尋
$posts->when($timeStart || $timeEnd, function ($repairs) use ($timeStart, $timeEnd) {
// 使用 "when" 方法檢查 $timeStart 或 $timeEnd 是否存在
$posts->when($timeStart, function ($repairs) use ($timeStart) {
// 如果 $timeStart 存在,則執行這個閉包
// 在查詢中添加條件,要求 "created_at" 大於或等於 $timeStart
$posts->where('created_at', '>=', $timeStart);
});
$posts->when($timeEnd, function ($repairs) use ($timeEnd) {
// 如果 $timeEnd 存在,則執行這個閉包
// 在查詢中添加條件,要求 "created_at" 小於或等於 $timeEnd
$posts->where('created_at', '<=', $timeEnd);
});
});
這個例子用了三個 閉包,並引入 $timeStart 及 $timeEnd 等外部變數
$timeStart
或 $timeEnd
存在時,要進行後續判斷$timeStart
** 存在時,則向查詢中添加一個條件,要求 created_at
大於或等於 $timeStart
。$timeEnd
** 存在時,則向查詢中添加一個條件,要求 created_at
大於或等於 $timeEnd
。$posts->when($keyword, function ($posts) use ($keyword) {
// 使用 "when" 方法檢查 $keyword 是否存在
$posts->where(function ($posts) use ($keyword) {
// 在這個閉包中建立一個關鍵字的模糊搜尋條件
$posts
->orWhere('post_number', 'like', "%$keyword%") // 文章編號
->orWhere('post_title', 'like', "%$keyword%") // 文章標題
->orWhere('post_content', 'like', "%$keyword%") // 文章內容
->orWhere('author_name', 'like', "%$keyword%") // 作者名稱
->orWhereHas('comments', function ($commentQuery) use ($keyword) {
// 使用 "orWhereHas" 檢查關聯 "comments" 的內容
$commentQuery
->where('content', 'like', "%$keyword%");
})
->orWhereHas('tags', function ($tagQuery) use ($keyword) {
// 使用 "orWhereHas" 檢查關聯 "tags" 的標籤名稱
$tagQuery
->where('name', 'like', "%$keyword%");
});
});
});
這裡用了四個閉包,四個閉包都使用外部引進的同一個 $keyword變數,可以用顏色判斷一下他們彼此間的關係。
程式碼說明:
when
方法:這是 Laravel 提供的一個條件式方法,用於根據給定的條件執行閉包內的操作。在這個例子中,如果 $keyword
存在(不為空或不為 null),則執行閉包內的操作。$posts
,這是一個資料庫查詢建構器(Eloquent 查詢)。外層閉包主要負責根據條件添加搜尋條件。where
方法建立了一個複雜的搜尋條件。這個內層閉包接受一個參數 $posts
,這是相同的資料庫查詢建構器,用於添加搜尋條件。orWhere
方法:這是用於在查詢中添加 OR 條件的方法。在這個例子中,我們使用 orWhere
方法多次,分別對不同的欄位進行模糊搜尋。orWhereHas
方法:這是用於檢查關聯模型的方法。我們在這個例子中使用 orWhereHas
來檢查關聯的 comments
和 tags
,並在這些關聯上進行額外的搜尋。這段程式碼的閉包使用讓你能夠根據關鍵字的存在與否,動態地建立複雜的搜尋條件。如果關鍵字存在,則會根據不同的欄位進行模糊搜尋,同時也會檢查關聯模型上的內容。這種方式讓你可以靈活地執行不同類型的搜尋操作。